import Objects from "../../utils/objects";
import SpaceUnit from "../utils/space_unit";
import Log from "../../utils/log";

/**
 * SpaceDistanceMeasure 空间距离量测
 * @param viewer Cesium.Viewer
 * @constructor SpaceDistanceMeasure
 */
function SpaceDistanceMeasure(viewer) {
    this._viewer = viewer;

    SpaceDistanceMeasure._HANDLER = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);

    this._positions = [];

    this._polylineEntityId = (new Date().getTime() + (Math.random() * 1000).toFixed(0)).toString();
    this._polylineEntity = null;
    this._pointEntities = [];
    this._labelEntities = [];
    this._totalDistanceLabelEntity = null;

    this._lastCartesian = null;
    this._totalDistance = 0;

    this._stopped = false;
    SpaceDistanceMeasure.MEASURERS.push(this);
}

SpaceDistanceMeasure._HANDLER = null;
SpaceDistanceMeasure.MEASURERS = [];

Object.defineProperties(SpaceDistanceMeasure.prototype, {
    stopped: {
        get: function() {
            return this._stopped;
        }
    }
});

SpaceDistanceMeasure.newInstance = function(viewer) {
    return new SpaceDistanceMeasure(viewer);
};

SpaceDistanceMeasure.lastMeasurer = function() {
    if (SpaceDistanceMeasure.MEASURERS.length === 0) {
        throw new Error("尚未拥有实例！！！");
    }

    return SpaceDistanceMeasure.MEASURERS[SpaceDistanceMeasure.MEASURERS.length - 1];
};

SpaceDistanceMeasure.prototype.start = function() {
    let that = this;
    SpaceDistanceMeasure._HANDLER.setInputAction(evt => {
        let cartesian3 = that._getCartesian3FromPosition(evt.position, [that._polylineEntity, that._totalDistanceLabelEntity], that._pointEntities, that._labelEntities);
        if (Objects.isNull(cartesian3)) {
            return;
        }
        that._step(cartesian3);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};

SpaceDistanceMeasure.prototype._step = function(cartesian3) {
    if (Objects.isNull(cartesian3)) {
        throw new Error("cartesian3 should not be null");
    }

    if (this._positions.length === 0) {
        this._pointEntities.push(this._createPointEntity(cartesian3));
        this._labelEntities.push(this._createLabelEntity(cartesian3, "起点", true));
        this._totalDistanceLabelEntity = this._createLabelEntity(cartesian3, Objects.EMPTY_STRING, true);
        this._positions.push(cartesian3);
    } else {
        this._totalDistanceLabelEntity.show = false;
        this._positions.push(cartesian3);

        let distance = Cesium.Cartesian3.distance(cartesian3, this._lastCartesian);
        this._totalDistance += distance;
        let text = SpaceUnit.METER.formatToM(distance, 2);

        this._pointEntities.push(this._createPointEntity(cartesian3));
        this._labelEntities.push(this._createLabelEntity(Cesium.Cartesian3.midpoint(this._lastCartesian, cartesian3, new Cesium.Cartesian3()), text, false));
    }

    this._lastCartesian = cartesian3;
    if (this._positions.length === 2) {
        this._polylineEntity = this._createPolylineEntity();
    }

    if (Objects.notNull(this._polylineEntity)) {
        this._totalDistanceLabelEntity.show = true;
        this._totalDistanceLabelEntity.label.text = SpaceUnit.METER.formatToM(this._totalDistance, 2);
        this._totalDistanceLabelEntity.position = this._lastCartesian;
    }
};

SpaceDistanceMeasure.prototype.stop = function() {
    SpaceDistanceMeasure._HANDLER.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this._stopped = true;
};

SpaceDistanceMeasure.prototype.destroy = function() {
    let that = this;
    this._viewer.entities.remove(this._polylineEntity);
    this._pointEntities.forEach(entity => that._viewer.entities.remove(entity));
    this._labelEntities.forEach(entity => that._viewer.entities.remove(entity));
    this._viewer.entities.remove(this._totalDistanceLabelEntity);
    SpaceDistanceMeasure.MEASURERS = SpaceDistanceMeasure.MEASURERS.filter(measurer => measurer !== this);
    SpaceDistanceMeasure._HANDLER.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
};

SpaceDistanceMeasure.destroyAll = function() {
    SpaceDistanceMeasure.MEASURERS.forEach(measurer => measurer.destroy());
    if (Objects.notNull(SpaceDistanceMeasure._HANDLER) && !SpaceDistanceMeasure._HANDLER.isDestroyed()) {
        Log.debug("start destroy SpaceDistanceMeasure._HANDLER...");
        SpaceDistanceMeasure._HANDLER.destroy();
    }
    SpaceDistanceMeasure.MEASURERS = [];
};

/**
 * 创建多边线 Entity
 * @returns {Cesium.Entity} 多边线 Entity
 * @private
 */
SpaceDistanceMeasure.prototype._createPolylineEntity = function() {
    let that = this;
    return this._viewer.entities.add(
        new Cesium.Entity({
            id: this._polylineEntityId,
            polyline: {
                positions: new Cesium.CallbackProperty(() => that._positions, false),
                show: true,
                disableDepthTestDistance: 0,
                material: Cesium.Color.YELLOW,
                width: 3,
                clampToGround: true
            }
        })
    );
};

/**
 * 创建 点 Entity
 * @param cartesian3 位置
 * @returns {Cesium.Entity} 点 Entity
 * @private
 */
SpaceDistanceMeasure.prototype._createPointEntity = function(cartesian3) {
    if (Objects.isNull(cartesian3)) {
        return;
    }

    return this._viewer.entities.add(
        new Cesium.Entity({
            position: cartesian3,
            point: {
                color: Cesium.Color.SKYBLUE,
                pixelSize: 10,
                outlineColor: Cesium.Color.YELLOW,
                outlineWidth: 3
            }
        })
    );
};

/**
 * 创建 标签 Entity
 * @param cartesian3 标签位置
 * @param text 标签文本
 * @param needOffset 是否向下偏移
 * @returns {Cesium.Entity|null} 标签 Entity
 * @private
 */
SpaceDistanceMeasure.prototype._createLabelEntity = function(cartesian3, text, needOffset = false) {
    if (Objects.isNull(cartesian3)) {
        return null;
    }

    return this._viewer.entities.add(
        new Cesium.Entity({
            position: cartesian3,
            label: {
                text: Objects.ensureNotNull(text),
                font: "24px Helvetica",
                fillColor: Cesium.Color.SKYBLUE,
                outlineColor: Cesium.Color.BLACK,
                outlineWidth: 2,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                pixelOffset: needOffset ? new Cesium.Cartesian2(0, 20) : null
            }
        })
    );
};

/**
 * 从屏幕位置得到 Cartesian3
 * @param position windowPosition
 * @param noPickEntityArray
 * @returns {null|*}
 * @private
 */
SpaceDistanceMeasure.prototype._getCartesian3FromPosition = function(position, ...noPickEntityArray) {
    let that = this;

    let picks = this._viewer.scene.drillPick(position);
    this._viewer.render();

    let getOnModel = false;
    let cartesian3 = null;

    if (this._viewer.scene.pickPositionSupported) {
        for (let i = 0; i < picks.length; i++) {
            let pickedObject = picks[i];
            for (let j = 0; j < noPickEntityArray.length; j++) {
                let noPickEntity = noPickEntityArray[j];
                if (that._hasPickedModel(pickedObject, noPickEntity)) {
                    getOnModel = true;
                }
            }
        }
    }

    if (getOnModel) {
        cartesian3 = this._viewer.scene.pickPosition(position);
    } else {
        let ray = this._viewer.camera.getPickRay(position);
        if (Objects.isNull(ray)) {
            return null;
        }
        cartesian3 = this._viewer.scene.globe.pick(ray, this._viewer.scene);
    }
    return cartesian3;
};

SpaceDistanceMeasure.prototype._hasPickedModel = function(pickedObject, noPickEntity) {
    if (Objects.isNull(noPickEntity)) {
        return true;
    }

    if (Objects.notNull(pickedObject.id)) {
        let entity = pickedObject.id;
        if (entity === noPickEntity) {
            return false;
        }
    }

    if (Objects.notNull(pickedObject.primitive)) {
        let primitive = pickedObject.primitive;
        if (primitive === noPickEntity) {
            return false;
        }
    }

    return true;
};

export default SpaceDistanceMeasure;
